package com.example.controller;

import java.io.File;
import java.lang.reflect.Modifier;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.model.User;
import com.example.tcls.DelegateMoo;
import com.example.tcls.Moo;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

@RestController
@RequestMapping("api")
public class ApiController {
	
	private static int flag = 0;

	/**
	 * https://blog.csdn.net/m0_37556444/article/details/106695202
	 bytebuddy的redifine和rebase只能重定义没有被jvm加载的类，如果试图redifine已经加载的类，会抛出异常
	 
	 bytebuddy提供了一个类型池的概念，我们不仅可以redifine classpath路径下的类，也可以从外部文件，jar包，网络redefine，我们需要一个ClassFileLocator去指导bytebuddy从那找到我们需要redefine的字节码文件
	 
	 defineMethod("helloWorld", String.class, Modifier.PUBLIC).withParameter(String.class, "arg1")

	 * @return
	 * @throws Exception
	 */
	@RequestMapping("info")
	public String info() throws Exception {
		//已经加载过的类不能重新定义
		
		Class<?> dynamicType = new ByteBuddy().redefine(User.class).make().load(ClassLoader.getSystemClassLoader())
				.getLoaded();
		
		System.err.println(dynamicType.getSimpleName());

		return "info";
	}
	
	@RequestMapping("delegate")
	public String DelegateMoo() throws Exception{
		Moo moo = new ByteBuddy()
	            .subclass(Moo.class)
	            .method(ElementMatchers.named("Moo").or(ElementMatchers.named("Moo1")))
	            .intercept(MethodDelegation.to(DelegateMoo.class))
	            .make()
	            .load(ClassLoader.getSystemClassLoader())
	            .getLoaded()
	            .newInstance();

//	    System.out.println("moo:" +  moo.Moo("tinysakura", 21));
	    System.out.println("moo1:" + moo.Moo1("tinysakura"));
		return "here";
	}
	
	
	@RequestMapping("delegate-dynamic")
	public String DelegateMooDynamic() throws Exception{
		System.err.println("delegate-dynamic-invoke--");
		
		//方式一 生成class 并使用Class.forName
		//方式二 直接使用load(ClassLoader.getSystemClassLoader()).getLoaded()
		
		// 优化 判断是否已经加载过，加过后直接执行
		Class<?> dynamicClazz = null;
		if(flag==0) {			
			try {
				Class.forName("com.example.DelegateMoo");
			} catch (Exception e) {
//				dynamicClazz = 
						new ByteBuddy()
						.subclass(Object.class)
						.name("com.example.DelegateMoo")
						.defineMethod("Moo1", String.class, Modifier.PUBLIC).withParameter(String.class, "param1")
						.intercept(FixedValue.value("Hello World!"))
						.defineMethod("Moo", String.class, Modifier.PUBLIC).withParameter(String.class, "param1").withParameter(Integer.class, "param2")
						.intercept(FixedValue.value("Hello World!"))
						.make()
						.saveIn(new File("target/classes"));//写入文件
//						.load(ClassLoader.getSystemClassLoader())
//						.getLoaded();
				
//				System.err.println("DelegateMoo.hashcode="+dynamicClazz.hashCode());
				
				
			}
			flag = 1;
		}
		
		//优化方案 将此方法写入到BEAN中
		Moo moo = new ByteBuddy()
				.subclass(Moo.class)
//            .method(ElementMatchers.named("Moo1"))
				.method(ElementMatchers.named("Moo").or(ElementMatchers.named("Moo1")))
				.intercept(MethodDelegation.to(Class.forName("com.example.DelegateMoo").newInstance()))
//				.intercept(MethodDelegation.to(dynamicClazz.newInstance()))//这种写法会报错
//            .intercept(MethodDelegation.to(DelegateMoo.class))
				.make()
				.load(ClassLoader.getSystemClassLoader())
				.getLoaded()
				.newInstance();
		System.out.println("moo1:" + moo.Moo1("tinysakura"));
		System.out.println("moo:" +  moo.Moo("tinysakura", 21));
		
		
		return "dynamic";
	}
}
